/* Extract files from archive */

#include "tar.h"
#include "args.h"
#include "dir.h"
#include "tapeio.h"
#include "table.h"
#include "extract.h"


static int ConfirmObject(char c, char *name, CatInfo *InfoBlk) {
  if (!ConfirmActions || QuietExecution) {
    return (1);
  }
  printf("%c ", c);
  if (Verbose)
    VerboseContents(InfoBlk);
  printf("%s: ", name);
  return (Decision('n') == 'y') ? 1 : 0;
} /* ConfirmObject */


static int CheckPath(char *name, int truedir) {
  char *cp;

  if (*(cp = name + strlen(name) - 1) != FS_DIRSEP && truedir &&
      Block.Header.linkflag != LF_DIR)
    return(0);

  *cp = '\0';
  if (MakeDir(name) >= 0) {
    if (!truedir) {
      *cp = FS_DIRSEP;
    }
    return (cp[1] == '\0');
  }

  *cp = FS_DIRSEP;
  for (cp = name; *cp; cp++) {
    if (*cp != FS_DIRSEP) {
      continue;
    }
    *cp = '\0';
    if (MakeDir(name) < 0) {
      perror(name);
      *cp = FS_DIRSEP;
      return (0);
    }
    *cp = FS_DIRSEP;
  }
  if (truedir && cp[-1] == FS_DIRSEP) {
    cp[-1] = '\0';
    return 1;
  }
  return 0;
} /* CheckPath */


static void GetObject(CatInfo *InfoBlk) {
  char *leaf,*browse,*filename;
  char save;
  FILE *ofile;
  int ExtentNo;
  long blocks, bytes;
  char buf[RECORDSIZE];

  if (!isArchie || Block.Header.archieflag != ARC_FLAG)
    name_to_fs(Block.Header.name);
  if (!ConfirmObject('x', Block.Header.name, InfoBlk)) {
    passtape(InfoBlk->Length);
    return;
  }
  if (CheckPath(Block.Header.name,1)) {
    goto set_attr;
  }
  leaf = Block.Header.name;
  for (browse = leaf; *browse; browse++) {
    if (*browse == FS_DIRSEP) {
      leaf = browse;
    }
  }
  if (*leaf == FS_DIRSEP) {
    leaf++;
    save = *leaf;
    *leaf = 0;
    CheckPath(Block.Header.name,0);
    *leaf = save;
  }
  if (Block.Header.linkflag == LF_EXTENT ||
      Block.Header.linkflag == LF_LASTEXTENT) {
    sscanf(Block.Header.ExtentNo,"%o",&ExtentNo);
  } else {
    ExtentNo = 0;
  }
  if (Block.Header.compressed == CF_COMPRESSED) {
    ChkScrapName();
    filename = ScrapNameZ;
    if (ExtentNo == 1) {
      compress_cleanup();
    }
  } else {
    filename = Block.Header.name;
  }
  if ((ofile = fopen(filename, ExtentNo > 1 ? "ab" : "wb")) == NULL) {
    fprintf(stderr, "tar: %s - cannot %s\n",filename,
            ExtentNo > 1 ? "open" : "create");
    passtape(InfoBlk->Length);
    return;
  }
  blocks = ((bytes = InfoBlk->Length) + RECORDSIZE-1)/RECORDSIZE;
  if (Verbose) {
    fprintf(stderr, "x %s, %ld bytes, ", Block.Header.name, bytes);
    PrintBlocks(stderr, blocks);
    if (Block.Header.compressed == CF_COMPRESSED)
      fprintf(stderr," (%2d%% compressed)",compression);
    if (ExtentNo > 0)
      fprintf(stderr," (extent #%d)",ExtentNo);
    fputc('\n',stderr);
  }
  for (; blocks-- > 0; bytes -= RECORDSIZE) {
    readtape(buf);
    if (bytes > RECORDSIZE) {
      if (fwrite(buf, RECORDSIZE, 1, ofile) < 1) {
        fprintf(stderr,"tar: %s: HELP - extract write error\n",Block.Header.name);
        Terminate(5);
      }
      continue;
    }
    if (fwrite(buf, 1, (int)bytes, ofile) < bytes) {
      fprintf(stderr,"tar: %s: HELP - extract write error\n",Block.Header.name);
      Terminate(6);
    }
  }
  fclose(ofile);
  if (Block.Header.compressed == CF_COMPRESSED &&
      Block.Header.linkflag != LF_EXTENT) {
    if (!ExecuteCommand(DecompressTemplate,ScrapNameZ,Block.Header.name,1))
      return;
    compress_cleanup();
  }
set_attr:
  if (Block.Header.linkflag != LF_EXTENT) {
    SetAttributes(
      DoNotExtractFileDates ?
        4 /* write file attributes */ :
        1 /* write load address, execution address, attributes */,
      Block.Header.name, (int)InfoBlk->Attr,
      (int)InfoBlk->LoadAddress, (int)InfoBlk->ExecAddress);
    if (DoNotExtractFileDates) {
      SetFileType(Block.Header.name, (int)((InfoBlk->LoadAddress >> 8) & 0xFFF));
    }
  }
} /* GetObject */


void ExtractFiles(void) {
  CatInfo InfoBlk;
  char startdir[256];
  
#if WITH_PWD
  GetCurrentDir(startdir);
#else
  startdir[0] = 0;
#endif
  for (;;) {
    getdir(&InfoBlk);
    if (endtape())
      break;
    switch (Block.Header.linkflag) {
      case LF_LASTEXTENT:
      case LF_EXTENT: {
        int ExtentNo;
        
        sscanf(Block.Header.ExtentNo,"%o",&ExtentNo);
        if (!MultipleVolumes && ExtentNo != 1) {
          fprintf(stderr,"tar: extent #%d of split file %s ignored\n",
            ExtentNo, Block.Header.name);
          passtape(InfoBlk.Length);
          break;
        }
        MultipleVolumes = 1;
        }
      case LF_NORMAL:
      case LF_OLDNORMAL:
      case LF_DIR:
        if (!MatchesNextArg(startdir)) {
          if (VeryVerbose) {
            fprintf(stderr, "- %s\n", Block.Header.name);
          }
          passtape(InfoBlk.Length);
        } else {
          GetObject(&InfoBlk);
        }
        break;
      default:
        break;
    }
  }
} /* ExtractFiles */
